package com.lock.utils;

import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.map.LRUMap;
import com.lock.utils.thread.ThreadUtils;

import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 防止重复提交工具类
 *
 * @author lcf
 */
public class LockUtils {
    private static LRUMap<String, Submit> cacheList = new LRUMap<>(100);
    private static Lock lock = new ReentrantLock();

    /**
     * 加锁
     *
     * @param id   唯一值
     * @param time 锁过期时长
     * @param unit 时间单位
     */
    public static void lock(String id, long time, TimeUnit unit) {
        lock.lock();

        try {
            if (cacheList.containsKey(id)) {
                Submit submit = getItem(id);
                if (submit != null && submit.getTime() != null && submit.getTime() <= System.currentTimeMillis()) {
                    cacheList.remove(id);
                } else {
                    throw new RuntimeException("您点的太快啦");
                }
            }

            cacheList.put(id, new Submit(time, unit, Thread.currentThread().getId()));

            ThreadUtils.addTask(LockUtils::removeOutTimeKey);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 加锁
     *
     * @param id 唯一值
     */
    public static void lock(String id) {
        lock.lock();

        try {
            if (cacheList.containsKey(id)) {
                throw new RuntimeException("您点的太快啦");
            }

            cacheList.put(id, new Submit(Thread.currentThread().getId()));

            ThreadUtils.addTask(LockUtils::removeOutTimeKey);
        }finally {
            lock.unlock();
        }
    }

    /**
     * 解锁
     * @param id 唯一值
     */
    public static void unlock(String id) {
        Submit submit = getItem(id);
        if (submit != null && Objects.equals(submit.getThreadId(), Thread.currentThread().getId())) {
            cacheList.remove(id);
        }
    }

    /**
     * 获取实体
     */
    private static Submit getItem(String id) {
        return cacheList.get(id);
    }

    /**
     * 锁实体
     *
     * @author lcf
     */
    @Data
    @NoArgsConstructor
    private static class Submit {
        /**
         * 锁时长
         */
        private Long time;
        /**
         * 线程id
         */
        private Long threadId;

        private Submit(long time, TimeUnit unit, long threadId) {
            this.time = System.currentTimeMillis() + unit.toMillis(time);
            this.threadId = threadId;
        }

        private Submit(long threadId) {
            this.threadId = threadId;
            this.time = -1L;
        }
    }

    /**
     * 遍历移除已过期锁
     */
    private static void removeOutTimeKey() {
        if (MapUtils.isEmpty(cacheList)) {
            return;
        }

        long currentTimeMillis = System.currentTimeMillis();

        Iterator<Map.Entry<String,Submit>> iterator = cacheList.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String,Submit> entry = iterator.next();
            Submit value = entry.getValue();

            if (value.getTime() > 0 && value.getTime() <= currentTimeMillis) {
                iterator.remove();
            }
        }
    }
}
